﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Kokomo.Mirror.CliMetadata.Tables;
using Kokomo.Mirror.CliMetadata.Signatures;

namespace Kokomo.Mirror.CliMetadata
{
	class CliPropertyInfo : PropertyInfo
	{
		public CliPropertyInfo(
			PropertyRow propertyRow,
			CliMemberBasicInfo basicInfo
			)
		{
			this._propertyRow = propertyRow;
			this._signature = (PropertySignature)basicInfo.module.GetSignature(this._propertyRow.Type);
			this.basicInfo = basicInfo;
			//this._customAttributeData = new Lazy<IList<CustomAttributeData>>(this.LoadCustomAttributeData);
		}

		private PropertyRow _propertyRow;
		private PropertySignature _signature;
		private CliMemberBasicInfo basicInfo;
		#region Attributes

		//private Lazy<IList<CustomAttributeData>> _customAttributeData;
		private IList<CustomAttributeData> LoadCustomAttributeData()
		{
			return this.basicInfo.module.GetCustomAttributesFor(this.MetadataToken);
		}
		public override IList<CustomAttributeData> GetCustomAttributesData()
		{
			//return this._customAttributeData.Value;
			return this.LoadCustomAttributeData();
		}
		#endregion

		public override int MetadataToken
		{
			get { return this.basicInfo.token; }
		}

		public override Module Module
		{
			get { return this.basicInfo.module; }
		}

		public override MemberTypes MemberType
		{
			get { return MemberTypes.Property; }
		}

		public override PropertyAttributes Attributes
		{
			get { return this._propertyRow.Flags; }
		}


		public override bool CanRead
		{
			get { return (object)this.GetGetMethod(true) != null; }
		}

		public override bool CanWrite
		{
			get { return (object)this.GetSetMethod(true) != null; }
		}

		public override MethodInfo[] GetAccessors(bool nonPublic)
		{
			MethodInfo getMethod = this.GetGetMethod(nonPublic);
			MethodInfo setMethod = this.GetSetMethod(nonPublic);

			List<MethodInfo> accessors = new List<MethodInfo>();
			if ((object)getMethod != null) accessors.Add(getMethod);
			if ((object)setMethod != null) accessors.Add(setMethod);

			return accessors.ToArray();
		}

		private MethodInfo GetSemanticMethod(MethodSemanticsAttributes semantics, bool nonPublic)
		{
			return this.basicInfo.module.QuerySemanticMethods(
				this.MetadataToken,
				this.DeclaringType,
				this.ReflectedType,
				semantics,
				nonPublic
				).FirstOrDefault();
		}

		public override MethodInfo GetGetMethod(bool nonPublic)
		{
			return this.GetSemanticMethod(MethodSemanticsAttributes.Getter, nonPublic);
		}

		public override ParameterInfo[] GetIndexParameters()
		{
			MethodInfo getMethod = this.GetGetMethod(true);
			if ((object)getMethod != null) return getMethod.GetParameters();

			MethodInfo setMethod = this.GetSetMethod(true);
			if ((object)setMethod != null)
			{
				ParameterInfo[] parameters = setMethod.GetParameters();
				if (parameters.Length > 0)
				{
					ParameterInfo[] indexParameters = new ParameterInfo[parameters.Length - 1];
					Array.Copy(parameters, 0, indexParameters, 0, indexParameters.Length);
					return indexParameters;
				}
			}

			return new ParameterInfo[0];
		}

		public override MethodInfo GetSetMethod(bool nonPublic)
		{
			return this.GetSemanticMethod(MethodSemanticsAttributes.Setter, nonPublic);
		}

		public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture)
		{
			throw new NotImplementedException();
		}

		public override Type PropertyType
		{
			get
			{
				return this._signature.type.ResolveType(
					this.basicInfo.module,
					true,
					this.DeclaringType.GetGenericArguments(),
					Type.EmptyTypes
					);
			}
		}

		public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture)
		{
			throw new NotImplementedException();
		}

		public override Type DeclaringType
		{
			get { return this.basicInfo.declaringType; }
		}

		public override object[] GetCustomAttributes(Type attributeType, bool inherit)
		{
			throw new NotImplementedException();
		}

		public override object[] GetCustomAttributes(bool inherit)
		{
			throw new NotImplementedException();
		}

		public override bool IsDefined(Type attributeType, bool inherit)
		{
			return this.basicInfo.module.IsAttributeDefinedOn(this.MetadataToken, attributeType, inherit);
		}

		public override string Name
		{
			get { return this.basicInfo.module.Metadata.GetString(this._propertyRow.Name); }
		}

		public override Type ReflectedType
		{
			get { return this.basicInfo.reflectedType; }
		}

		public PropertyInfo Reflect()
		{
			Type declaring = this.DeclaringType.Reflect();
			PropertyInfo reflected = declaring.GetProperty(this.Name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			return reflected;
		}
	}
}
